package com.suixuan.support.dao.hibernate;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

/**
 * 
 * 
 * @Title: BaseDaoImpl.java
 * @Package: com.suixuan.support.dao.hibernate
 * @Description: 数据访问层基类 封装了一些常用的方法
 * 
 * @Version V1.0
 * 
 */
@Repository
@SuppressWarnings({ "unchecked", "rawtypes" })
public class BaseDaoImpl<T, PK extends Serializable> extends HibernateDaoSupport implements Serializable {

	private static final long serialVersionUID = -200912072134L;

	/**
	 * 批量保存
	 * 
	 * @param entityList
	 *            {@link List}
	 */
	public void batchSave(List<T> entityList) {
		Assert.notNull(entityList);
		int count = 0;
		Session session = this.getSession();
		for (T entity : entityList) {
			session.save(entity);
			if (++count % 20 == 0) {
				session.flush();
				session.clear();
			}
		}
	}

	/**
	 * 属性值是否被占用
	 * 
	 * @param entityClass
	 *            {@link Class}
	 * @param propertyName
	 *            属性名
	 * @param checkValue
	 *            属性值
	 * @return True=已使用 False=未使用
	 */
	public boolean checkPropertyValueUsed(Class<?> entityClass, String propertyName, final Object checkValue) {
		final String hql = "select 1 from " + entityClass.getName() + " obj where obj." + propertyName + " = :objvalue";

		return (Boolean) this.getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return null != session.createQuery(hql).setParameter("objvalue", checkValue).uniqueResult();
			}
		});
	}

	/**
	 * 检查属性值是否存在
	 * 
	 * @param entityClass
	 *            {@link Class}
	 * @param propertyName
	 *            属性名
	 * @param checkValue
	 *            属性值
	 * @param idParaName
	 *            主键字段名
	 * @param excludeId
	 *            排除标识
	 * @return True=已使用 False=未使用
	 */
	public boolean checkPropertyValueUsed(Class<?> entityClass, String propertyName, Object checkValue,
			String idParaName, PK excludeId) {
		final Map<String, Object> paras = new HashMap<String, Object>();
		StringBuffer sb = new StringBuffer("select 1 from " + entityClass.getName() + " obj where obj." + propertyName
				+ " = :objvalue");
		paras.put("objvalue", checkValue);
		if (excludeId != null) {
			sb.append(" and obj." + idParaName + " != :idvalue");
			paras.put("idvalue", excludeId);
		}
		final String hql = sb.toString();
		return (Boolean) this.getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return null != session.createQuery(hql).setProperties(paras).uniqueResult();
			}
		});
	}

	public void clear() {
		getHibernateTemplate().clear();
	}

	/**
	 * Example查询 count 配合 list(T example, String[] excludeProperty, int offset,
	 * int limit, Order... order)进行分页
	 * 
	 * @param example
	 *            T
	 * @param excludeProperty
	 *            排除属性
	 * @return long
	 */
	public long count(T example, final String[] excludeProperty) {
		return sum(example, excludeProperty, MatchMode.ANYWHERE);
	}

	/**
	 * Example查询 count
	 * 
	 * @param example
	 *            T类实例
	 * @param excludeProperty
	 *            排除查询的属性
	 * @param matchMode
	 *            {@link MatchMode}
	 * @return count数
	 */
	public long count(T example, String[] excludeProperty, MatchMode matchMode) {
		return this.sum(example, excludeProperty, matchMode);
	}

	/**
	 * 查询所有数据量
	 * 
	 * @param entityClass
	 *            {@link Class}
	 * @return long
	 */
	public long countAll(final Class<T> entityClass) {
		final String hql = "select count(*) from " + entityClass.getName();
		return uniqueResult(hql, null, Long.class);
	}

	/**
	 * 删除
	 * 
	 * @param entityClass
	 *            {@link Class}
	 * @param id
	 *            主键Id
	 */
	public void delete(Class<T> entityClass, Serializable id) {
		T instance = find(entityClass, id);
		if (instance != null)
			delete(instance);
	}

	/**
	 * 删除
	 * 
	 * @param entity
	 *            T
	 */
	public void delete(T entity) {
		getHibernateTemplate().delete(entity);
	}

	/**
	 * 根据唯一字段删除记录
	 * 
	 * @param entityClass
	 *            {@link Class}
	 * @param propertyName
	 *            属性名
	 * @param propertyValue
	 *            属性值
	 */
	public void deleteByUniquePropertyValue(Class<T> entityClass, final String propertyName, final Object propertyValue) {

		final String hql = "delete " + entityClass.getName() + " where " + propertyName + " = :" + propertyName;
		this.getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				session.createQuery(hql).setParameter(propertyName, propertyValue).executeUpdate();
				return null;
			}
		});

	}

	/**
	 * 对象实例从session缓存清除
	 * 
	 * @param entity
	 *            T
	 */
	public void evict(T entity) {
		getHibernateTemplate().evict(entity);
	}

	/**
	 * 执行sql
	 * 
	 * @param sql
	 * @return 执行条数
	 */
	public int execute(final String sql) {
		return (Integer) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return session.createQuery(sql).executeUpdate();
			}
		});
	}

	/**
	 * 执行sql
	 * 
	 * @param sql
	 * @param values
	 *            ？参数值
	 * @return 执行条数
	 */
	public int execute(final String sql, final Object[] values) {
		return (Integer) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query q = session.createQuery(sql);
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						q.setParameter(i, values[i]);
					}
				}
				return q.executeUpdate();
			}
		});
	}

	/**
	 * 查找
	 * 
	 * @param hql
	 *            需要执行的hql
	 * @param paras
	 *            如果没有 参数 直接为null或者空集合
	 * @return
	 */
	protected List<T> executeFind(final String hql, final Map<String, Object> paras) {
		return (List<T>) this.getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.createQuery(hql);
				if (paras != null) {
					query.setProperties(paras);
				}
				return query.list();
			}
		});
	}

	/**
	 * 查找
	 * 
	 * @param hql
	 *            需要执行的hql
	 * @param paras
	 *            如果没有 参数 直接为null或者空集合
	 * @param limit
	 *            最大返回条数
	 * @param offset
	 *            起始行数
	 * @return
	 */
	protected List<T> executeFind(final String hql, final Map<String, Object> paras, final int offset, final int limit) {
		return (List<T>) this.getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.createQuery(hql);
				if (paras != null) {
					query.setProperties(paras);
				}
				return query.setFirstResult(offset).setMaxResults(limit).list();
			}
		});
	}

	/**
	 * 批量更新
	 * 
	 * @param hql
	 *            需要执行的hql
	 * @param paras
	 *            如果没有 参数 直接为null或者空集合
	 * @return
	 */
	protected int executeUpdate(final String hql, final Map<String, Object> paras) {
		return (Integer) this.getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return (Integer) session.createQuery(hql).setProperties(paras).executeUpdate();
			}
		});
	}

	/**
	 * 主键查询
	 * 
	 * @param entityClass
	 *            {@link Class}
	 * @param id
	 *            主键值
	 * @return T
	 */
	public T find(Class<T> entityClass, Serializable id) {
		return (T) getHibernateTemplate().get(entityClass, id);
	}

	/**
	 * 根据唯一值查询对象
	 * 
	 * @param entityClass
	 *            {@link Class}
	 * @param propertyName
	 *            属性名
	 * @param propertyValue
	 *            属性值
	 * @return 对象
	 */
	public T findByUniquePropertyValue(Class<T> entityClass, final String propertyName, final Object propertyValue) {
		final String hql = " from " + entityClass.getName() + " obj where obj." + propertyName + " = :" + propertyName;

		return (T) this.getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(final Session session) throws HibernateException, SQLException {
				return session.createQuery(hql).setParameter(propertyName, propertyValue).uniqueResult();
			}
		});
	}

	/**
	 * 根据唯一值查询对象
	 * 
	 * @param propertyName
	 *            属性名
	 * @param propertyValue
	 *            属性值
	 * @return 对象
	 */
	public T findByUniquePropertyValue(String propertyName, Object propertyValue) {

		return this.findByUniquePropertyValue(
				(Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0],
				propertyName, propertyValue);
	}

	public void flush() {
		getHibernateTemplate().flush();
	}

	/**
	 * 查询
	 * 
	 * @param entityClass
	 *            {@link Class}
	 * @param pk
	 *            主键字段
	 * @param id
	 *            主键集合
	 * @return {@link List}
	 */
	public List<T> list(Class<T> entityClass, String pk, List<PK> id) {
		final String hql = "from " + entityClass.getName() + " entity where entity." + pk + " in (:id)";
		Map<String, Object> paras = new HashMap<String, Object>();
		paras.put("id", id);
		return (List<T>) this.executeFind(hql, paras);
	}

	/**
	 * Exmaple查询
	 * 
	 * @param example
	 *            T
	 * @param excludeProperty
	 *            排除属性
	 * @param offset
	 *            起始数
	 * @param limit
	 *            偏移量
	 * @param matchMode
	 *            查询匹配模式 {@link MatchMode}
	 * @param order
	 *            {@link Order}
	 * @return {@link List}
	 */
	public List<T> list(T example, String[] excludeProperty, int offset, int limit, MatchMode matchMode, Order... order) {
		Assert.notNull(example, "Example entity must not be null");
		DetachedCriteria c = DetachedCriteria.forClass(example.getClass());

		Example expl = Example.create(example).enableLike(matchMode);
		if (excludeProperty != null) {
			for (String exclude : excludeProperty) {
				expl.excludeProperty(exclude);
			}
		}
		c.add(expl);

		for (Order o : order) {
			c.addOrder(o);
		}

		return getHibernateTemplate().findByCriteria(c, offset, limit);
	}

	/**
	 * Example查询
	 * 
	 * @param example
	 *            T
	 * @param excludeProperty
	 *            排除属性
	 * @param offset
	 *            分页起始数
	 * @param limit
	 *            偏移量
	 * @param order
	 *            {@link Order}
	 * @return {@link List}
	 */
	public List<T> list(T example, String[] excludeProperty, int offset, int limit, Order... order) {
		return list(example, excludeProperty, offset, limit, MatchMode.ANYWHERE, order);
	}

	/**
	 * Example查询
	 * 
	 * @param example
	 *            T类实例
	 * @param excludeProperty
	 *            排除查询的属性
	 * @param matchMode
	 *            {@link MatchMode}
	 * @param orders
	 *            {@link Order}
	 * @return {@link List}
	 */
	public List<T> list(T example, String[] excludeProperty, MatchMode matchMode, Order... orders) {

		return this.list(example, excludeProperty, 0, -1, matchMode, orders);
	}

	/**
	 * 查询所有集合
	 * 
	 * @param entityClass
	 *            {@link Class}
	 * @param order
	 *            {@link Order}
	 * @return {@link List}
	 */
	public List<T> listAll(Class<T> entityClass, Order... order) {
		DetachedCriteria c = DetachedCriteria.forClass(entityClass);
		for (Order o : order) {
			c.addOrder(o);
		}
		return getHibernateTemplate().findByCriteria(c);
	}

	public void merge(T entity) {
		super.getHibernateTemplate().merge(entity);

	}

	/**
	 * 保存
	 * 
	 * @param entity
	 *            T
	 */
	public void save(T entity) {
		getHibernateTemplate().save(entity);
	}

	/**
	 * 保存或更新
	 * 
	 * @param entity
	 */
	public void saveOrUpdate(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);

	}

	@Resource
	public void setSessionFactoryOverride(SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}

	/**
	 * 查询数据集合
	 * 
	 * @param example
	 *            T
	 * @param excludeProperty
	 *            排除属性
	 * @param matchMode
	 *            查询匹配模式 {@link MatchMode}
	 * @return int 数据量
	 */
	public int sum(final T example, final String[] excludeProperty, final MatchMode matchMode) {
		Assert.notNull(example, "Example entity must not be null");
		return (Integer) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Criteria c = session.createCriteria(example.getClass());
				Example expl = Example.create(example).enableLike(matchMode);
				if (excludeProperty != null) {
					for (String exclude : excludeProperty) {
						expl.excludeProperty(exclude);
					}
				}
				c.add(expl);
				return c.setProjection(Projections.rowCount()).uniqueResult();
			}
		});
	}

	/**
	 * 查找唯一结果
	 * 
	 * @param hql
	 *            需要执行的hql
	 * @param paras
	 *            如果没有 参数 直接为null或者空集合
	 * @return Object
	 */
	protected Object uniqueResult(final String hql, final Map<String, Object> paras) {
		return this.getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.createQuery(hql);
				if (paras != null) {
					query.setProperties(paras);
				}
				return query.uniqueResult();
			}
		});
	}

	/**
	 * 查找唯一结果
	 * 
	 * @param hql
	 *            需要执行的hql
	 * @param paras
	 *            如果没有 参数 直接为null或者空集合
	 * @param clazz
	 *            返回类型
	 * @return 具体类型
	 */
	public <C> C uniqueResult(final String hql, final Map<String, Object> paras, Class<C> clazz) {
		return (C) this.uniqueResult(hql, paras);
	}

	/**
	 * 更新
	 * 
	 * @param entity
	 *            T
	 */
	public void update(T entity) {
		getHibernateTemplate().update(entity);

	}

}
